package fileio

import (
	"os"
	"strings"
	"sync/atomic"
	"syscall"
)

type OSFile interface {
	Fd() uintptr
	Name() string
	ReadAt(b []byte, off int64) (n int, err error)
	WriteAt(b []byte, off int64) (n int, err error)
	Close() error
}

type HandleIOFile struct {
	OSFile
	UserData    interface{}
	HandleIOErr func(file *HandleIOFile, err error)

	readBytes  int64
	writeBytes int64
}

func (f *HandleIOFile) ReadAt(b []byte, off int64) (n int, err error) {
	n, err = f.OSFile.ReadAt(b, off)
	if err != nil && f.HandleIOErr != nil {
		f.HandleIOErr(f, err)
	}

	if n > 0 {
		atomic.AddInt64(&f.readBytes, int64(n))
	}

	return
}

func (f *HandleIOFile) WriteAt(b []byte, off int64) (n int, err error) {
	n, err = f.OSFile.WriteAt(b, off)
	if err != nil && f.HandleIOErr != nil {
		f.HandleIOErr(f, err)
	}

	if n > 0 {
		atomic.AddInt64(&f.writeBytes, int64(n))
	}

	return
}

func (f *HandleIOFile) CallHandleIOErr(err error) {
	if f.HandleIOErr == nil {
		return
	}

	f.HandleIOErr(f, err)
}

func (f *HandleIOFile) GetStatus() (readBytes, writeBytes int64) {
	return atomic.LoadInt64(&f.readBytes), atomic.LoadInt64(&f.writeBytes)
}

func FileExists(fileName string) (bool, error) {
	_, err := os.Stat(fileName)
	if err == nil {
		return true, nil
	}

	if os.IsNotExist(err) {
		return false, nil
	}

	return false, err
}

func IsBlockDev(fileName string) bool {
	stat, err := os.Stat(fileName)
	if err != nil {
		return false
	}

	if stat.Mode()&os.ModeDevice != 0 {
		return true
	}

	return false
}

func IsEIO(err error) bool {
	if err == nil {
		return false
	}

	return strings.Contains(err.Error(), syscall.EIO.Error()) ||
		strings.Contains(err.Error(), syscall.EROFS.Error()) ||
		strings.Contains(err.Error(), "no data available")
}

func IsDir(path string) bool {
	info, err := os.Stat(path)
	if err != nil {
		return false
	}

	return info.IsDir()
}
